package com.agile.leetcode.AESandRSAandMd5;

import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author:ChenZhangKun
 * @Date: 2021/8/24 15:49
 */
public class Base64Test {
    public static void main(String[] args) throws Exception {
        // base64编码原理
        // 个例子，假设我们要对字符串 S.H 进行编码：
        //
        //将其转换成十六进制为 53、2e、48
        //
        //再将十六进制转换成二进制，分别为 01010011、00101110、01001000。这里不足 8 个比特的高位补 0 即可。
        //
        //将其每6个比特分为一组，分别为 010100、110010、111001、001000
        //
        //将其转换成十进制得到，20、50、57、8
        //
        //再根据表格中的转换关系转换可得，U、y、5、I
        //
        //换句话说，字符串 S.H 通过 Base64 算法编码之后的结果为 Uy5I 。
        byte[] bytes = Base64.getEncoder().encode("S.H".getBytes());
        System.out.println(new String(bytes));
        byte[] decode = Base64.getDecoder().decode("Uy5I");
        System.out.println(new String(decode));
        encode("S.H");
        decode("Uy5I");
    }

    private static Map<Integer, Object> map = new HashMap<>();

    static {
        int j = 65;
        // 初始化大写
        for (int i = 0; i < 26; i++) {
            map.put(i, (char) j++);
        }
        System.out.println(map);
        // 初始化小写
        j = 97;
        for (int i = 26; i < 52; i++) {
            map.put(i, (char) j++);
        }
        System.out.println(map);
        j = 0;
        // 初始化数字
        /*for (int i = 52; i < 62; i++) {
            map.put(i,  );
        }*/
        map.put(52, '0');
        map.put(53, '1');
        map.put(54, '2');
        map.put(55, '3');
        map.put(56, '4');
        map.put(57, '5');
        map.put(58, '6');
        map.put(59, '7');
        map.put(60, '8');
        map.put(61, '9');


        map.put(62, '+');
        map.put(63, '/');
        System.out.println(map);
    }

    /**
     * 模拟base64算法
     * 步骤
     * str->十六进制str->二进制串（不足8为的字符高位补0）->每6个bit为一组转为十进制（末尾不足6位低位补0）->拿到数字从映射表获取字符->编码完成
     *
     * @param str
     * @throws UnsupportedEncodingException
     */
    public static void encode(String str) throws UnsupportedEncodingException {
        // 将字符转为十六进制
        String hex = strToHex(str);
        System.out.println(hex);
        byte[] bytes = hexToByte(hex);
        // 转为二进制字符串
        String binStr = byteArrToBinStr(bytes);
        System.out.println(binStr);
        // 不足六位高位补0
        String[] split = binStr.split(",");
        StringBuilder builder = new StringBuilder();
        for (String s : split) {
            while (s.length() < 8) {
                // 高位补0
                s = "0" + s;
            }
            builder.append(s);
        }
        // 拿到完整二进制串
        String binaryStr = builder.toString();
        System.out.println(binaryStr);
        builder = new StringBuilder();
        // 8为一组，转为十进制
        for (int i = 0; i < binaryStr.length(); i += 6) {
            String substring = null;
            // 截取
            if (i + 6 < binaryStr.length()) {
                substring = binaryStr.substring(i, i + 6);
            } else {
                // 最后几位
                substring = binaryStr.substring(i);
                // 末尾不足6位补0
                while (substring.length() < 6) {
                    substring += "0";
                }
            }
            System.out.println(substring);
            // 转为10进制
            int parseInt = Integer.parseInt(substring, 2);
            // 拿到对应的值
            Object o = map.get(parseInt);
            builder.append(o);
        }
        // 查看是否只有两个字母的情况，规定三个字母用x补充
        if (binaryStr.length() < 24) {
            builder.append("=");
        }
        // 拿到编码值
        System.out.println(builder.toString());
    }

    /**
     * 模拟md5解码
     *
     * @param str
     * @return
     * @throws Exception
     */
    public static String decode(String str) throws Exception {
        // 有=移除
        if (str.contains("=")) {
            str = str.replaceAll("=", "");
        }
        StringBuilder builder = new StringBuilder();
        // 拿到对应的数字
        for (int i = 0; i < str.length(); i++) {
            Object c = str.charAt(i);
            if (c instanceof Integer) {
                c = (Integer) c;
            }
            // 从map中得到该数字
            for (Map.Entry<Integer, Object> entry : map.entrySet()) {
                // 拿到value
                if (Objects.equals(entry.getValue(), c)) {
                    // 拿到key
                    Integer key = entry.getKey();
                    // 转为二进制
                    String binaryString = Integer.toBinaryString(key);
                    // 不足六位高位补0
                    while (binaryString.length() < 6) {
                        binaryString = "0" + binaryString;
                    }
                    builder.append(binaryString);
                    break;
                }
            }
        }
        String string = builder.toString();
        System.out.println(string);
        builder = new StringBuilder();
        // 没八位一组转成十六进制
        for (int i = 0; i < string.length(); i += 8) {
            String subString = null;
            if (i + 8 > str.length()) {
                subString = string.substring(i, i + 8);
            } else {
                subString = string.substring(i);
                while (subString.length() < 8) {
                    // 末尾补0
                    subString += "0";
                }
            }
            // 先将字符串转为10进制
            int j = Integer.parseInt(subString, 2);
            // 10进制转为16进制
            String s = Integer.toString(j, 16);

            builder.append(s);
        }
        String s = builder.toString();
        System.out.println(s);
        // 十六进制串转为普通字符串
        String toString = hexStringToString(s);
        System.out.println(toString);
        return toString;
    }

    /**
     * 16进制转换成为string类型字符串
     *
     * @param s
     * @return
     */
    public static String hexStringToString(String s) {
        if (s == null || s.equals("")) {
            return null;
        }
        s = s.replace(" ", "");
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "UTF-8");
            new String();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }

    /**
     * 字符串转十六进制
     *
     * @param str
     * @return
     */
    public static String strToHex(String str) {
        //
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            // 转为数字
            int c = (int) str.charAt(i);
            // 转为16进制
            String string = Integer.toHexString(c);
            result.append(string);
        }
        return result.toString();
    }

    /**
     * 十六进制转二进制数组
     *
     * @param hex
     * @return
     * @throws UnsupportedEncodingException
     */
    public static byte[] hexToByte(String hex) throws UnsupportedEncodingException {
        byte[] arrB = hex.getBytes("UTF-8");
        // 两个16进制字符表示一个字节长度
        // 因为一个字节等于8bit（bit = 二进制位）
        // 一个16进制字符 需要 4个二进制才能表示
        // 所以两个16进制字符等于一个字节
        //
        int len = arrB.length;
        byte[] arrOut = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 上面得到的字节数组中，一个字符是一个长度，两个长度作为一个字符
            String s = new String(arrB, i, 2, "UTF-8");
            System.out.println(s);
            arrOut[i / 2] = (byte) Integer.parseInt(s, 16);
        }
        return arrOut;
    }

    /**
     * byte数组转换为二进制字符串,每个字节以","隔开
     **/
    public static String byteArrToBinStr(byte[] b) {
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            result.append(Long.toString(b[i] & 0xff, 2) + ",");
        }
        return result.toString().substring(0, result.length() - 1);
    }
}
